// Copyright (c) [2019] [huazhonghua]
// [理解] is licensed under the Mulan PSL v1.
// You can use this software according to the terms and conditions of the Mulan PSL v1.
// You may obtain a copy of Mulan PSL v1 at:
//     http://license.coscl.org.cn/MulanPSL
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v1 for more details.

#ifndef PARSING_H
#define PARSING_H

#include <wchar.h>
#include <wctype.h>
#include <time.h>

#include "structure.h"
#include "basic.h"
#include "labels.h"
#include "execution.h"

#include "debug.h"

struct ParseInfo {
	FILE* file;
	wint_t wc;
	Node* context_stack;
};

typedef struct ParseInfo ParseInfo;

void read_spaces(ParseInfo* info) {
	while(info->wc != WEOF && iswspace(info->wc)) {
		info->wc = fgetwc(info->file);
	}
}

int is_occurred_error(Node* node) {
	return 0;
}

int is_operation(Node* node) {
	return get_linked(node, OPERATION) != NULL;
}

int get_precedence(Node* node) {
	Node* precedence = get_linked(node, PRECEDENCE);
	if(precedence != NULL) return precedence->part;
	if(is_tier_1(node, LIST)) return 1;
	return 0;
}

Node* build_expression(Node* all, List* line, int start, int end);

Node* build_binary_like_expression(Node* all, List* line, int start, int end, int last, Node* operation) {
	Node* left = build_expression(all, line, start, last);
	Node* right = build_expression(all, line, last + 1, end);

	Node* expression = create_node(all);
	link_node(expression, LEFT, left);
	link_node(expression, OPERATION, operation);
	link_node(expression, RIGHT, right);

	return expression;
}

Node* build_right_unary_expression(Node* all, List* line, int start, int end, int last, Node* operation) {
	if(last > start) {
		// error
	}

	Node* expression = create_node(all);
	link_node(expression, OPERATION, operation);

	Node* right = build_expression(all, line, last + 1, end);
	link_node(expression, RIGHT, right);

	return expression;
}

Node* build_return(Node* all, List* line, int start, int end, int last, Node* operation) {
	if(last > start) {
		// error
	}

	Node* expression = create_node(all);
	link_node(expression, OPERATION, operation);

	if(end - last - 1 > 0) {
		Node* right = build_expression(all, line, last + 1, end);
		link_node(expression, RIGHT, right);
	}

	return expression;
}

Node* build_expression(Node* all, List* line, int start, int end) {
	int count = end - start;
	if(count <= 0) {
printf("build exp error\n");
//error
		return NULL;
	}

	Node* item;
	int precedence;
	int max_precedence = 0;
	int last;
	for(int i = start; i < end; ++i) {
		item = line->nodes[i];
		precedence = get_precedence(item);
		if(precedence >= max_precedence) {
			max_precedence = precedence;
			last = i;
		}
	}

	Node* expression;
	Node* left;
	Node* right;
	Node* last_op = line->nodes[last];

	if(is_tier_1(last_op, RETURN)) {
		expression = build_return(all, line, start, end, last, last_op);
	}
	else if(end - start == 1) {
		expression = line->nodes[start];
	}
	else if(is_instance_of(last_op, LIST)) {
		if(last < end - 1) {
			// error
		}
		
		left = build_expression(all, line, start, last);

		Node* goal_expression = get_linked(all, GOAL_EXPRESSION);
		expression = create_node(all);
		link_node(expression, OPERATION, goal_expression);
		link_node(expression, LEFT, left);
		link_node(expression, ARGUMENT, last_op);
	}
	else if(is_tier_1(last_op, IF) || is_tier_1(last_op, UNORDERED) || is_tier_1(last_op, RETURN) || is_tier_1(last_op, ADD_WAY)) {
		expression = build_right_unary_expression(all, line, start, end, last, last_op);
	}
	else if(is_tier_1(last_op, NEGATION)) {
		if(last > start) {
			// error
		}

		right = build_expression(all, line, last + 1, end);
		Node* operation = get_linked(right, OPERATION);

		if(is_tier_1(operation, NEGATION)) {
			expression = get_linked(right, RIGHT);
			free_node(right);
		}
		else if(is_instance_of(right, INTEGER)) {
			expression = get_create_int(all, right->part * -1);
		}
		else {
			expression = create_node(all);
			link_node(expression, OPERATION, last_op);
			link_node(expression, RIGHT, right);
		}
	}
	else if(is_instance_of(last_op, BINARY_OPERATION)) {
		expression = build_binary_like_expression(all, line, start, end, last, last_op);
	}
	else {
		printf("unknown op error\n");
	}

	return expression;
}

Node* read_string(Node* all, ParseInfo* info) {
	info->wc = fgetwc(info->file);
	List* list = create_list(64);
	while(info->wc != WEOF && info->wc != L'"') {
		add_to_list(list, get_create_wchar_t(all, info->wc));
		info->wc = fgetwc(info->file);
	}

	if(info->wc == WEOF) {
		printf("Unexpected EOF.");
		return NULL;
	}

	info->wc = fgetwc(info->file);
	Node* chars = create_node(all);
	chars->other.pointer = list;
	Node* string = get_create_string_by_node(all, chars, NULL);
	free_list(list);
	free_node(chars);

	return string;
}

Node* find_in_all(Node* all, Node* chars) {
	Node* string = get_create_string_by_node(all, chars, NULL);

	Node* represents = get_linked(string, REPRESENT);
	if(represents == NULL) return NULL;

	List* list = represents->other.pointer;
	if(list == NULL) return NULL;

	if(list->length > 1) {
		for(int i = 0; i < list->length; ++i) {
			Node* node = list->nodes[i];
			if(is_goal_instance(node)) return node;
		}
	}

	return list->length > 0 ? list->nodes[0] : NULL;
}

Node* add_variable(Node* all, Node* chars) {
	Node* string = get_create_string_by_node(all, chars, NULL);
	Node* variable = create_node(all);
	link_node(variable, STRING, string);
	link_node(variable, TYPE, get_linked(all, VARIABLE));
	return variable;
}

Node* get_create_first_part(Node* all, Node* context, Node* chars) {
	Node* node = find_in_all(all, chars);
	if(node != NULL) return node;

	return add_variable(all, chars);
}

Node* read_list(Node* all, ParseInfo* info);

Node* build_node_from_chars(Node* all, ParseInfo* info, List* line, Node* chars) {
	Node* node;
	List* char_list = chars->other.pointer;
	if(char_list->length == 0) {
		//printf("Empty character list.\n");
		return NULL;
	}
	else {
		wchar_t* c_str = to_c_string(chars);
		wchar_t* end;

		long num = wcstol(c_str, &end, 10);

		if(*end == L'\0') {
			node = get_create_int(all, (int)num);
		}
		else if(is_tier_1(get_list_tail(line), LINK_ACCESS)) {
			node = find_by_chars(all, all, chars, NULL);
		}
		else {
			node = get_create_first_part(all, get_list_node_tail(info->context_stack), chars);
		}

		free(c_str);

		clear_list(char_list);
	}

	return node;
}

Node* create_occurred_error(Node* all, int error_part) {
	Node* node = create_node(all);
	link_node(node, TYPE, get_linked(all, OCCURRED_ERROR));
	link_node(node, ERROR, get_chained(all, (int[]){ERROR, INSTANCE, error_part}, 3));
	return node;
}

Node* read_expression(Node* all, ParseInfo* info) {
	Node* node;
	List* line = create_list(1);
	wint_t close_char = WEOF;

	if(info->wc == L'(') {
		close_char = L')';
		info->wc = fgetwc(info->file);
	}
	else if(info->wc == L'[') {
		close_char = L']';
		info->wc = fgetwc(info->file);
	}

	read_spaces(info);

	Node* chars = create_list_node(all, 64);
	List* char_list = chars->other.pointer;
	int error_occurred = 0;

	while(info->wc != WEOF && info->wc != L',' && info->wc != L'}' && info->wc != close_char) {
		if(iswspace(info->wc)) {
			printf("Unexpected space.");
			break;
		}

		int keep_space = 0;

		if(info->wc == L'{') {
			Node* tail = 
			node = read_list(all, info);
		}
		else if(info->wc == L'(') {
			node = read_expression(all, info);
		}
		else if(info->wc == L'[') {
			add_to_list(line, get_linked(all, ARRAY_ACCESS));
			node = read_expression(all, info);
		}
		else if(info->wc == L'"') {
			node = read_string(all, info);
		}
		else if(info->wc == L'`') {
			info->wc = fgetwc(info->file);
			while(info->wc != WEOF && info->wc != L'`') {
				add_to_list(char_list, get_create_wchar_t(all, info->wc));
				info->wc = fgetwc(info->file);
			}

			if(info->wc == WEOF) {
				node = create_occurred_error(all, UNEXPECTED_EOF);
			}
			else {
				node = build_node_from_chars(all, info, line, chars);
				info->wc = fgetwc(info->file);
			}
		}
		else if(info->wc == L'.') {
			node = get_linked(all, LINK_ACCESS);
			keep_space = 1;
			info->wc = fgetwc(info->file);
		}
		else {
			if(info->wc == '/') {
				wint_t peek = fgetwc(info->file);
				if(peek == '/') {
					while(info->wc != WEOF && info->wc != L'\n') {
						info->wc = fgetwc(info->file);
					}
					read_spaces(info);
					continue;
				}
				else {
					add_to_list(char_list, get_create_wchar_t(all, L'/'));
					info->wc = peek;
				}
			}

			while(info->wc != WEOF && !iswspace(info->wc) && info->wc != L'.' && info->wc != L'(' && info->wc != L'[' &&
				info->wc != L',' &&	info->wc != L'}' && info->wc != close_char) {

				add_to_list(char_list, get_create_wchar_t(all, info->wc));
				info->wc = fgetwc(info->file);
			}
			node = build_node_from_chars(all, info, line, chars);
		}

		error_occurred = is_occurred_error(node);
		if(error_occurred) break;

		add_to_list(line, node);

		if(!keep_space) {
			read_spaces(info);
		}
	}

	if(!error_occurred && close_char != WEOF) {
		if(info->wc == close_char) {
			info->wc = fgetwc(info->file);
		}
		else {
			printf("Unexpected expression closing.");
			node = create_node(all);
			error_occurred = 1;
		}
	}

	if(!error_occurred) {
		node = build_expression(all, line, 0, line->length);
	}
	// need to free each node
	free_list(line);
	free_list_node(chars);

	return node;
}

Node* read_list(Node* all, ParseInfo* info) {
	Node* list_node = NULL;
	int has_open = 0;

	if(info->wc == L'{') {
		has_open = 1;
		list_node = create_list_node(all, 2);
		info->wc = fgetwc(info->file);
	}

	while(info->wc != WEOF && info->wc != L'}') {
		Node* node = read_expression(all, info);

		// check error

		if(has_open) {
			add_to_list_node(list_node, node);
		}
		else {
			make_runnable(all, node, 0);

			Node* request = create_request(all, info->context_stack, node);
link_node(all, REQUEST, request);
			prepare_request_for_waiting(request);


clock_t begin = clock();

			queue_work(all, request);

			wait_for_request_done(request);

clock_t end = clock();

double time_used = (double)(end - begin) / CLOCKS_PER_SEC;
printf("time %f\n", time_used);

			free_request_wait_data(request);

Node* r = get_linked(request, RETURN);

printf("done request ");
print_node(r);
printf("\n");
			
			// execute
			// dispose
		}

		if(info->wc == L',') {
			info->wc = fgetwc(info->file);
		}
	}

	if(has_open) {
		if(info->wc != L'}') {
			printf("Missing list closing.\n");
			// error, free
			return NULL;
		}
		else {
			info->wc = fgetwc(info->file);
		}
	}
	else {
		if(info->wc == L'}') {
			printf("Unexpected list closing.\n");
			// error, free
			return NULL;
		}
	}
	return list_node;
}

void parse(Node* all, ParseInfo* info) {
	info->wc = fgetwc(info->file);
	read_spaces(info);
	read_list(all, info);
}

#endif
